package com.ican.oc.common.hibernate;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.springframework.util.Assert;


/**
 * 封装扩展功能的Hibernate DAO泛型基类. 
 */
@SuppressWarnings("rawtypes")
public class OcHibernateDao<T, PK extends Serializable> extends OcSimpleHibernateDao<T, PK> {
	/**
	 * 用于Dao层子类的构造函数.
	 * 通过子类的泛型定义取得对象类型Class.
	 * eg.
	 * public class UserDao extends HibernateDao<User, Long>{
	 * }
	 */
	public OcHibernateDao() {
		super();
	}

	/**
	 * 用于省略Dao层, Service层直接使用通用HibernateDao的构造函数.
	 * 在构造函数中定义对象类型Class.
	 * eg.
	 * HibernateDao<User, Long> userDao = new HibernateDao<User, Long>(sessionFactory, User.class);
	 */
	public OcHibernateDao(final SessionFactory sessionFactory, final Class<T> entityClass) {
		super(sessionFactory, entityClass);
	}

	//-- 分页查询函数 --//
	/**
	 * 分页获取全部对象.
	 */
	public OcPage<T> getAll(final OcPage<T> page) {
		return findPage(page, null);
	}
	/**
	 * 按HQL分页查询.
	 * 
	 * @param page 分页参数
	 * @param hql hql语句.
	 * @param values 数量可变的查询参数,按顺序绑定.
	 * 
	 * @return 分页查询结果, 附带结果列表及所有查询输入参数.
	 */
	@SuppressWarnings("unchecked")
	public OcPage<T> findPage(final OcPage<T> page, final String hql, final Object... values) {
		Assert.notNull(page, "page不能为空");

		Query q = createQuery(hql, values);
		
		long totalCount = countHqlResult(hql, values);
		page.setTotalCount(totalCount);

		setPageParameterToQuery(q, page);

		List result = q.list();
		page.setResult(result);
		return page;
	}

	/**
	 * 按HQL分页查询.
	 * 
	 * @param page 分页参数. 注意不支持其中的orderBy参数.
	 * @param hql hql语句.
	 * @param values 命名参数,按名称绑定.
	 * 
	 * @return 分页查询结果, 附带结果列表及所有查询输入参数.
	 */
	@SuppressWarnings("unchecked")
	public OcPage<T> findPage(final OcPage<T> page, final String hql, final Map<String, ?> values) {
		Assert.notNull(page, "page不能为空");

		Query q = createQuery(hql, values);

		long totalCount = countHqlResult(hql, values);
		page.setTotalCount(totalCount);

		setPageParameterToQuery(q, page);

		List result = q.list();
		page.setResult(result);
		return page;
	}

	/**
     * 按sql分页查询.
     * 
     * @param page 分页参数
     * @param sql  sql语句.
     * @param values 数量可变的查询参数,按顺序绑定.
     * 
     * @return 分页查询结果, 附带结果列表及所有查询输入参数.
     */
    @SuppressWarnings("unchecked")
    public OcPage<T> findPageSQL(final OcPage<T> page, final String sql, final Object... values) {
        Assert.notNull(page, "page不能为空");

        SQLQuery q = createSqlQuery(sql, values);
        
        long totalCount = countSqlResult(sql, values);
        page.setTotalCount(totalCount);

        setPageParameterToQuery(q, page);

        List result = q.addEntity(entityClass).list();
        page.setResult(result);
        return page;
    }

    /**
     * 按sql分页查询.
     * 
     * @param page 分页参数. 注意不支持其中的orderBy参数.
     * @param sql hql语句.
     * @param values 命名参数,按名称绑定.
     * 
     * @return 分页查询结果, 附带结果列表及所有查询输入参数.
     */
    @SuppressWarnings("unchecked")
    public OcPage<T> findPageSQL(final OcPage<T> page, final String sql, final Map<String, ?> values) {
        Assert.notNull(page, "page不能为空");

        SQLQuery q = createSqlQuery(sql, values);

        long totalCount = countSqlResult(sql, values);
        page.setTotalCount(totalCount);

        setPageParameterToQuery(q, page);

        List result = q.addEntity(entityClass).list();
        page.setResult(result);
        return page;
    }
	
	/**
	 * 设置分页参数到Query对象,辅助函数.
	 */
	protected Query setPageParameterToQuery(final Query q, final OcPage<T> page) {

		Assert.isTrue(page.getPageSize() > 0, "Page Size must larger than zero");

		//hibernate的firstResult的序号从0开始
		q.setFirstResult(page.getFirst() - 1);
		q.setMaxResults(page.getPageSize());
		return q;
	}

	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	protected long countHqlResult(final String hql, final Object... values) {
		String countHql = prepareCountHql(hql);

		try {
			Long count = findUnique(countHql, values);
			return count;
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
	}
	
	/**
     * 执行count查询获得本次Hql查询所能获得的对象总数.
     * 
     * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
     */
    protected long countHqlResult(final String hql, final Map<String, ?> values) {
        String countHql = prepareCountHql(hql);
        try {
            Long count = findUnique(countHql, values);
            return count;
        } catch (Exception e) {
            throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
        }
    }
	
	/**
     * 执行count查询获得本次SQL查询所能获得的对象总数.
     * 
     * 本函数只能自动处理简单的SQL语句,复杂的SQL查询请另行编写count语句查询.
     */
    protected long countSqlResult(final String sql, final Object... values) {
        String countSql = prepareCountSql(sql);

        try {
            BigInteger bi = findUniqueSQL(countSql, values);
            return bi.longValue();
        } catch (Exception e) {
            throw new RuntimeException("hql can't be auto count, hql is:" + countSql, e);
        }
    }

	/**
	 * 执行count查询获得本次SQL查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的SQL语句,复杂的SQL查询请另行编写count语句查询.
	 */
	protected long countSqlResult(final String sql, final Map<String, ?> values) {
	    String countSql = prepareCountSql(sql);

		try {
			Long count = findUniqueSQL(countSql, values);
			return count;
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countSql, e);
		}
	}

	private String prepareCountHql(String orgHql) {
		String fromHql = orgHql;
		//select子句与order by子句会影响count查询,进行简单的排除.
		fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
		fromHql = StringUtils.substringBefore(fromHql, "order by");

		String countHql = "select count(*) " + fromHql;
		return countHql;
	}
	
	private String prepareCountSql(String orgSql) {
        String fromSql = orgSql;
        //select子句与order by子句会影响count查询,进行简单的排除.
        fromSql = "from " + StringUtils.substringAfter(fromSql, "from");
        fromSql = StringUtils.substringBefore(fromSql, "order by");

        String countHql = "select count(*) " + fromSql;
        return countHql;
    }
}
